﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Windows.Media;

namespace Sunny.Controls
{
	public abstract class HierarchicalSelector<ItemType, IdType> : System.Windows.Controls.WrapPanel where IdType:struct
	{
		protected abstract System.Collections.Generic.IEnumerable<ItemType> TopItems { get; }

		protected abstract System.Collections.Generic.IEnumerable<ItemType> GetChildItems(ItemType item);

		protected abstract ItemType GetItem(object id);

		protected abstract ItemType GetParentItem(ItemType item);

		protected abstract string ItemIdProperty { get; }

		protected abstract string ItemTextProperty { get; }

		protected abstract IdType DefaultId { get; }

		private IdType[] _AllowIds;
		public IdType[] AllowIds
		{
			get
			{
				return this._AllowIds;
			}
			set
			{
				this._AllowIds = value;
				this._AllAllowItems = null;
				if (this.Children.Count > 0)
					this.SelectedId = this.SelectedId;
			}
		}

		private ItemType[] _AllAllowItems;
		protected ItemType[] AllAllowItems
		{
			get
			{
				if(this._AllAllowItems == null)
				{
					if (this.AllowIds == null)
						return null;
					System.Collections.Generic.List<ItemType> items = new System.Collections.Generic.List<ItemType>();
					foreach (IdType id in this.AllowIds)
					{
						for (ItemType item = this.GetItem(id); item != null; item = this.GetParentItem(item))
							items.Add(item);
						items.AddRange(this.GetAllLevelChildItems(this.GetItem(id)));
					}
					this._AllAllowItems = items.ToArray();
				}
				return this._AllAllowItems;
			}
		}

		//private IdType[] _AllAllowIds;
		//protected IdType[] AllAllowIds
		//{
		//	get
		//	{
		//		if(this._AllAllowIds==null)
		//		{
		//			if (this.AllowIds == null)
		//				return null;
		//			System.Collections.Generic.List<IdType> ids = new System.Collections.Generic.List<IdType>();
		//			foreach (IdType id in this.AllowIds)
		//			{
		//				for (ItemType item = this.GetItem(id); item != null; item = this.GetParentItem(item))
		//					ids.Add((IdType)item.GetType().GetProperty(this.ItemIdProperty).GetValue(item, null));
		//				ids.AddRange(this.GetAllLevelChildIds(id));
		//			}
		//		}
		//		return this._AllAllowIds;
		//	}
		//}

		//protected IdType[] GetAllLevelChildIds(IdType id)
		//{
		//	System.Collections.Generic.List<IdType> ids = new System.Collections.Generic.List<IdType>();
		//	foreach(ItemType item in this.GetChildItems(this.GetItem(id)))
		//	{
		//		IdType childId = (IdType)item.GetType().GetProperty(this.ItemIdProperty).GetValue(item, null);
		//		ids.Add(childId);
		//		ids.AddRange(this.GetAllLevelChildIds(childId));
		//	}
		//	return ids.ToArray();
		//}

		protected System.Collections.Generic.IEnumerable<ItemType> GetSameLevelItems(ItemType item)
		{
			ItemType parentItem = this.GetParentItem(item);
			System.Collections.Generic.IEnumerable<ItemType> items;
			if (parentItem == null)
				items = this.TopItems;
			else items = this.GetChildItems(parentItem);
			if (this.AllAllowItems != null)
				items = items.Where(i => this.AllAllowItems.Contains(i));
			return items;
		}

		protected ItemType[] GetAllLevelChildItems(ItemType item)
		{
			System.Collections.Generic.List<ItemType> items = new List<ItemType>();
			foreach (ItemType childItem in this.GetChildItems(item))
			{
				items.Add(childItem);
				items.AddRange(this.GetAllLevelChildItems(childItem));
			}
			//if (this.AllAllowItems != null)
			//	items = items.Where(i => this.AllAllowItems.Contains(i)).ToList();
			return items.ToArray();
		}

		private ItemType[] _AllLevelChildItemsOfSelectedItem;
		protected ItemType[] AllLevelChildItemsOfSelectedItem
		{
			get
			{
				if (this._AllLevelChildItemsOfSelectedItem == null)
				{
					if (this.SelectedId == null)
						this._AllLevelChildItemsOfSelectedItem = new ItemType[0];
					else this._AllLevelChildItemsOfSelectedItem = this.GetAllLevelChildItems(this.SelectedItem);
				}
				return this._AllLevelChildItemsOfSelectedItem;
			}
		}

		private object[] _ResideIdsOfSelectedItem;
		public object[] ResideIdsOfSelectedItem
		{
			get
			{
				if (this._ResideIdsOfSelectedItem == null)
					if (this.SelectedId == null)
						this._ResideIdsOfSelectedItem = new object[0];
					else this._ResideIdsOfSelectedItem = this.AllLevelChildItemsOfSelectedItem.Select(i => typeof(ItemType).GetProperty(this.ItemIdProperty).GetValue(i, null)).Concat(new object[] { this.SelectedId }).ToArray();
				return this._ResideIdsOfSelectedItem;
			}
		}


		public bool AllowNull { get; set; }

		public bool SelectingId;

		public System.Nullable<IdType> SelectedId
		{
			get
			{
				return (System.Nullable<IdType>)this.LastNotNullDropDownList?.SelectedValue;
			}
			set
			{
				this.SelectingId = true;
				try
				{
					this.Children.Clear();
					if(value == null)
					{
						if (this.AllowIds == null)
							this.AddList(this.TopItems, null);
						else this.AddList(this.TopItems.Where(i => this.AllAllowItems.Contains(i)), null);
						return;
					}
					ItemType item = this.GetItem(value);
					System.Collections.Generic.IEnumerable<ItemType> items = this.GetSameLevelItems(item);
					if (this.AllowIds != null)
						items = items.Where(i => this.AllAllowItems.Contains(i));
					this.AddList(items, value);
					for(ItemType parentItem=this.GetParentItem(item);parentItem!= null;parentItem=this.GetParentItem(parentItem))
					{
						DropDownList list = this.CreateList(this.GetSameLevelItems(parentItem));
						this.AddingList = true;
						list.SelectedItem = parentItem;
						this.AddingList = false;
						this.Children.Insert(0, list);
					}
				}
				finally
				{
					this.SelectingId = false;
				}
			}
		}

		private ItemType _SelectedItem;
		public ItemType SelectedItem
		{
			get
			{
				return (ItemType)this.LastNotNullDropDownList?.SelectedItem;
			}
		}

		public DropDownList LastNotNullDropDownList
		{
			get
			{
				return this.DropDownLists.LastOrDefault(d => d.SelectedValue != null);
			}
		}

		public System.Collections.Generic.IEnumerable<DropDownList> DropDownLists
		{
			get
			{
				return this.Children.OfType<DropDownList>();
			}
		}

		public HierarchicalSelector()
		{
		}

		protected override void OnRender(DrawingContext dc)
		{
			if (this.Children.Count == 0)
				if (this.AllowNull)
					this.SelectedId = null;
				else this.SelectedId = this.DefaultId;

			base.OnRender(dc);
		}

		//protected void Initialize()
		//{
		//	if (this.AllowNull)
		//	{
		//		if (this.SelectedId == null || this.SelectedItem == null)
		//		{
		//			this.Controls.Add(this.CreateList(this.TopItems, null));
		//			return;
		//		}
		//	}
		//	else
		//	{
		//		if (this.SelectedId == null || this.SelectedItem == null)
		//		{
		//			System.Collections.Generic.IEnumerable<ItemType> items = this.TopItems;
		//			while (items.Count() > 0)
		//			{
		//				ItemType firstItem = items.First();
		//				this.SelectedId = this.GetItemId(firstItem);
		//				items = this.GetChildItems(firstItem);
		//			}
		//		}
		//	}
		//	for (ItemType item = this.SelectedItem; item != null; item = this.GetParentItem(item))
		//		this.Controls.AddAt(0, this.CreateList(this.GetSameLevelItems(item), this.GetItemId(item)));
		//	System.Collections.Generic.IEnumerable<ItemType> childItems = this.GetChildItems(this.SelectedItem);
		//	if (this.AllowNull)
		//	{
		//		if (childItems.Count() > 0)
		//			this.Controls.Add(this.CreateList(childItems, null));
		//	}
		//	else for (System.Collections.Generic.IEnumerable<ItemType> items = childItems; items.Count() > 0; items = this.GetChildItems(items.First()))
		//			this.Controls.Add(this.CreateList(items, null));
		//	//this.OnClientResponseEnded = string.Format("function(){{$('input[name={0}')}}");
		//}

		protected DropDownList CreateList(System.Collections.Generic.IEnumerable<ItemType> items)
		{
			DropDownList list = new DropDownList();
			list.AllowNull = this.AllowNull;
			list.DisplayMemberPath = this.ItemTextProperty;
			list.SelectedValuePath = this.ItemIdProperty;
			list.ItemsSource = items;
			list.SelectionChanged += List_SelectionChanged;
			return list;
		}

		protected DropDownList AddList(System.Collections.Generic.IEnumerable<ItemType> items, object selectedId)
		{
			DropDownList list = this.CreateList(items);
			this.Children.Add(list);
			if (selectedId != null)
			{
				this.AddingList = true;
				list.SelectedValue = selectedId;
				this.AddingList = false;
			}
			return list;
		}

		protected bool AddingList;

		private void List_SelectionChanged(object sender, System.Windows.Controls.SelectionChangedEventArgs e)
		{
			if (this.AddingList)
				return;

			if (this.SelectingId)
				return;

			DropDownList list = (DropDownList)sender;
			for (int i = this.Children.Count - 1; i >= 0; i--)
				if (this.Children[i] == list)
					break;
				else this.Children.RemoveAt(i);
			if (list.SelectedValue == null)
				return;
			System.Collections.Generic.IEnumerable<ItemType> items = this.GetChildItems((ItemType)list.SelectedItem);
			while (items.Count() > 0)
			{
				if (this.AllowIds != null)
					items = items.Where(i => this.AllAllowItems.Contains(i));
				object selectedId = this.AllowNull ? null : typeof(ItemType).GetProperty(this.ItemIdProperty).GetValue(items.First(), null);
				list = this.AddList(items, selectedId);
				if (selectedId == null)
					return;
				items = this.GetChildItems((ItemType)list.SelectedItem);
				if (this.AllowIds != null)
					items = items.Where(i => this.AllAllowItems.Contains(i));
			}
		}
	}
}